#include<allegro5\allegro.h>
#include<allegro5\allegro5.h>
#include<allegro5\allegro_primitives.h>
#include<allegro5\timer.h>
#include<allegro5\keyboard.h>
#include<allegro5\allegro_image.h>
#include<allegro5\allegro_font.h>
#include<allegro5\allegro_ttf.h>
#include<cmath>
#include"globals.h"
#include"mappy_A5.h"

//======================================================
//global variables
//======================================================

const int WIDTH = 1024;
const int HEIGHT = 600;
const int FPS = 60;
const int TILE_WIDTH = 32;
const int TILE_HEIGHT = 32;
const float HERO_SPEED = 2;
const float GRAVITY = 0.25;

MY_STATE_MACHINE state_m (3);

//======================================================
//global functions
//======================================================
void change_state(MY_STATE_MACHINE &stm , GAME_STATES next);
void apply_gravity(MY_ELEMENT *element);
void check_ground_collision(MY_ELEMENT *element_array , int size);

int xOff = 0;
int yOff = 0;
int fix = 0;
int main(void)
{
	//======================================================
	//primitive variables
	//======================================================
	bool done = false;
	bool update_screen = false;
	
	//======================================================
	//allegro variables
	//======================================================
	ALLEGRO_DISPLAY *display = NULL;
	ALLEGRO_EVENT_QUEUE *event_queue = NULL;
	ALLEGRO_TIMER *timer = NULL;
	ALLEGRO_BITMAP *h_bitmap_standing = NULL;
	ALLEGRO_BITMAP *h_bitmap_running = NULL;
	ALLEGRO_TRANSFORM *transf = NULL;
	ALLEGRO_FONT *font18 = NULL;
	ALLEGRO_TRANSFORM camera;

	//======================================================
	//init allegro
	//======================================================
	if(!al_init())
		return -1;

	//======================================================
	//init & install addons
	//======================================================
	al_init_primitives_addon();
	al_install_keyboard();
	al_init_image_addon();
	al_init_font_addon();
	al_init_ttf_addon();

	//======================================================
	//assign values to allegro variables
	//======================================================
	display = al_create_display(WIDTH , HEIGHT);
	event_queue = al_create_event_queue();
	timer = al_create_timer(1.0/FPS);
	h_bitmap_standing = al_load_bitmap("mainchar_jumping.png");
	h_bitmap_running = al_load_bitmap("mainchar_running.png");
	font18 = al_load_font("arial.ttf",18,0);
	
	//======================================================
	//assign event sources
	//======================================================
	al_register_event_source(event_queue , al_get_keyboard_event_source());
	al_register_event_source(event_queue , al_get_display_event_source(display));
	al_register_event_source(event_queue , al_get_timer_event_source(timer));

	if(MapLoad("RockyMap2.FMP" , 1))
		return -5;
	//======================================================
	//start timer
	//======================================================
	al_start_timer(timer);

	//======================================================
	//my variables
	//======================================================
	MY_ELEMENT *my_elements[1];								// every elements on the current scene should be placed here
	int number_of_elements = 1;								// number of elements on the current scene
	

	// Hero graph for standing animation
	MY_GRAPHIC hero_standing (2 , 2 , 60 , 80 , h_bitmap_standing);
	hero_standing.frame_delay = 45;							// frame delay for hero standing animation

	// Hero graph for running animation
	MY_GRAPHIC hero_running (10 , 10 , 60 , 80 , h_bitmap_running , 2 , 9);
	hero_running.frame_delay = 4;							// frame delay for hero running animation

	MY_ELEMENT hero (1 , &hero_standing);					// creates hero element, parsing hero_standing as current animation
	hero.live = true;										// this element is alive
	hero.x = WIDTH/2;										// set element position x
	hero.y = HEIGHT/2;										// set element position y
	hero.bound_x = 12;										// set element collision boundary x
	hero.bound_y = 27;										// set element collision boundary y
	hero.show_collision_boundaries = false;					// choose not to display boundaries
	my_elements[0] = &hero;									// assign this element to first position of my_elements array (array of pointers)
	
	MY_CAMERA cam(WIDTH , HEIGHT);							// creates camera
	cam.scale_x = 1.0;										// zoom scale  x axis
	cam.scale_y = 1.0;										// zoom scale y axis

	MY_STATE_MACHINE state_machine(3);

	while(!done)
	{
		ALLEGRO_EVENT ev;
		al_wait_for_event(event_queue , &ev);
		if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
		{
			if(state_machine.current_state != GAME_OVER)
				change_state(state_machine , GAME_OVER);
			else
				done = true;
		}
		if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
		{
			switch(ev.keyboard.keycode)
			{
				case ALLEGRO_KEY_UP:
					keys[UP] = true;
					break;
				case ALLEGRO_KEY_DOWN:
					keys[DOWN] = true;
					break;
				case ALLEGRO_KEY_LEFT:
					keys[LEFT] = true;
					break;
				case ALLEGRO_KEY_RIGHT:
					keys[RIGHT] = true;
					break;
				case ALLEGRO_KEY_ESCAPE:
					keys[ESCAPE] = true;
					break;
				case ALLEGRO_KEY_F1:
					cam.scale_x += 0.5;
					cam.scale_y += 0.5;
					break;
				case ALLEGRO_KEY_F2:
					cam.scale_x -= 0.5;
					cam.scale_y -= 0.5;
					break;
				case ALLEGRO_KEY_F3:
					fix += 5;
				default:
					break;
			}
				
		}
		if(ev.type == ALLEGRO_EVENT_KEY_UP)
		{
			switch(ev.keyboard.keycode)
			{
				case ALLEGRO_KEY_UP:
					keys[UP] = false;
					if(state_machine.current_state == RUN_GAME)
					{
						hero.current_jump_power = 0;
						hero.can_jump_again = true;
					}
					break;
				case ALLEGRO_KEY_DOWN:
					keys[DOWN] = false;
					break;
				case ALLEGRO_KEY_LEFT:
					keys[LEFT] = false;
					break;
				case ALLEGRO_KEY_RIGHT:
					keys[RIGHT] = false;
					break;
				case ALLEGRO_KEY_ESCAPE:
					keys[ESCAPE] = false;
				default:
					break;
			}
		}
		if(ev.type == ALLEGRO_EVENT_TIMER)
		{
			switch(state_machine.current_state)
			{
				case MAIN_MENU:
					if(keys[ESCAPE])
					{
						change_state(state_machine , RUN_GAME);
					}
					break;
				case RUN_GAME:
					if(keys[RIGHT])
					{
						hero.vel_x = HERO_SPEED;
						hero.set_graphic(&hero_running);
						hero.graphic->invert_horizontal = false;
					}
					if(keys[LEFT])
					{
						hero.vel_x = -HERO_SPEED;
						hero.set_graphic(&hero_running);
						hero.graphic->invert_horizontal = true;
					}
					if(keys[UP])
					{
						hero.jump();
					}
					else if (hero.can_jump_again == false)
					{
						hero.can_jump_again = true;
					}
					if(keys[DOWN])
					{
						//hero.y += HERO_SPEED;

					}
					if(keys[ESCAPE])
					{
						change_state(state_machine , GAME_OVER);
					}
					if((!keys[RIGHT])&&(!keys[LEFT]))
					{
						hero.vel_x = 0;
						hero_standing.invert_horizontal = hero_running.invert_horizontal;
						hero.set_graphic(&hero_standing);
					}
					if((keys[RIGHT])&&(keys[LEFT]))
					{
						hero.vel_x = 0;
						hero.set_graphic(&hero_standing);
					}
					
					break;
				case GAME_OVER:
					if(keys[ESCAPE])
						done = true;
					break;
				default:
					break;
			}
			
			update_screen = true;
		}
		if(al_event_queue_is_empty(event_queue) && update_screen)					// drawing scene
		{
			update_screen = false;

			switch(state_machine.current_state)
			{
				case MAIN_MENU:
					al_identity_transform(&camera);
					al_use_transform(&camera);
					al_draw_textf(font18 , al_map_rgb(255, 255 , 255) , WIDTH/2 , HEIGHT/2 , ALLEGRO_ALIGN_CENTER , "TESTE DE COLISAO E CAMERA No. 2  BY JoaoCFJr");
					al_flip_display();
					al_clear_to_color(al_map_rgb(0 , 0 , 0));
					break;
				case RUN_GAME:
					check_ground_collision(my_elements[0] , number_of_elements);
					hero.update();
					al_identity_transform(&camera);
					al_use_transform(&camera);
					al_clear_to_color(al_map_rgb(140 , 220 , 255));
					cam.update(&camera , &hero , mapwidth*mapblockwidth , mapheight*mapblockheight);
					al_use_transform(&camera);
					MapDrawBG(0, 0 ,0 ,0, WIDTH + cam.x, HEIGHT);
					hero.draw();
					al_use_transform(&camera);
					al_flip_display();
					break;
				case GAME_OVER:
					al_identity_transform(&camera);
					al_use_transform(&camera);
					al_draw_textf(font18 , al_map_rgb(255, 255 , 255) , WIDTH/2 , HEIGHT/2 , ALLEGRO_ALIGN_CENTER , "FIM DO TESTE! =]");
					al_flip_display();
					al_clear_to_color(al_map_rgb(0 , 0 , 0));
					break;
				default:
					break;
			}
			
		}
	}
	//======================================================
	//destroy allegro variables
	//======================================================
	al_destroy_event_queue(event_queue);
	al_destroy_display(display);
	al_destroy_timer(timer);
	al_destroy_bitmap(h_bitmap_running);
	al_destroy_bitmap(h_bitmap_standing);
	MapFreeMem();
	return 0;
}

void check_ground_collision(MY_ELEMENT *element_array , int size)
{
	BLKSTR * block = NULL;
	float block_pos[2];
	float touching[2];
	bool should_apply_gravity;
	bool in_ladder = false;

	for(int i  = 0; i < size ; i++)
	{
		should_apply_gravity = true;
		//======================================================
		// Element going downward
		//======================================================	
		touching[0] = element_array[i].x + element_array[i].bound_x - 1;
		touching[1] = element_array[i].y + element_array[i].bound_y;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_y >= 0))
		{
			block = MapGetBlockInPixels(touching[0] , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			switch(block->user1)
			{
				case 0:
					break;
				case 1:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
				case 4:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
				case 5:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
			}
		}

		touching[0] = element_array[i].x;
		touching[1] = element_array[i].y + element_array[i].bound_y;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_y >= 0))
		{
			block = MapGetBlockInPixels(touching[0] , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			switch(block->user1)
			{
				case 2:
					if(touching[1] >=  block_pos[1]  + mapblockheight - (touching[0] - block_pos[0]) &&
						element_array[i].last_jump_height <=  block_pos[1] - element_array[i].bound_y + mapblockheight - (touching[0] - block_pos[0]))
					{
						element_array[i].y = block_pos[1] - element_array[i].bound_y + mapblockheight - (touching[0] - block_pos[0]);
						if(element_array[i].vel_x < 0)
						{
							element_array[i].vel_y =  -element_array[i].vel_x;
							in_ladder = true;
						}
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
				case 3:
					if(touching[1] >= block_pos[1] + (touching[0] - block_pos[0])&&
						element_array[i].last_jump_height <=  block_pos[1]  - element_array[i].bound_y + (touching[0]- block_pos[0]))
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y + (touching[0]- block_pos[0]);
						if(element_array[i].vel_x > 0)
						{
							element_array[i].vel_y =  element_array[i].vel_x;
							in_ladder = true;
						}
						
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
				case 5:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
				case 6:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
			
			}
		}

		touching[0] = element_array[i].x - element_array[i].bound_x + 1;
		touching[1] = element_array[i].y + element_array[i].bound_y;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_y >= 0))
		{
			block = MapGetBlockInPixels(touching[0] , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			switch(block->user1)
			{
				case 0:
					break;
				case 1:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
				case 4:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
				case 5:
					if(element_array[i].last_jump_height <= block_pos[1]  - element_array[i].bound_y)
					{
						element_array[i].y = block_pos[1]  - element_array[i].bound_y;
						should_apply_gravity = false;
						element_array[i].reset_jump();
					}
					break;
			}
	
		}

		//======================================================
		// Element going upward
		//======================================================	
		touching[0] = element_array[i].x + element_array[i].bound_x - 1;
		touching[1] = element_array[i].y - element_array[i].bound_y;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_y < 0))
		{
			block = MapGetBlockInPixels(touching[0] , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			switch(block->user1)
			{
				case 1:
						element_array[i].y = block_pos[1] + mapblockheight  + element_array[i].bound_y;
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
					break;
				case 5:
					if(touching[1] <=  block_pos[1] + (touching[0] - block_pos[0]))
					{
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
					}
					break;
			}
		}

		touching[0] = element_array[i].x;
		touching[1] = element_array[i].y - element_array[i].bound_y;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_y < 0))
		{
			block = MapGetBlockInPixels(touching[0] , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			switch(block->user1)
			{
				case 1:
						element_array[i].y = block_pos[1] + mapblockheight  + element_array[i].bound_y;
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
					break;
				case 2:
						element_array[i].y = block_pos[1] + mapblockheight  + element_array[i].bound_y;
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
						break;
				case 3:
						element_array[i].y = block_pos[1] + mapblockheight  + element_array[i].bound_y;
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
						break;
				case 4:
					if(touching[1] <=  block_pos[1] + mapblockheight - (touching[0] - block_pos[0]))
					{
						element_array[i].y = block_pos[1] + mapblockheight + element_array[i].bound_y - (touching[0] - block_pos[0]);
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
					}
					break;
				case 5:
					if(touching[1] <=  block_pos[1] + (touching[0] - block_pos[0]))
					{
						element_array[i].y = block_pos[1] + element_array[i].bound_y + (touching[0] - block_pos[0]);
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
					}
					break;
			}
		}

		touching[0] = element_array[i].x - element_array[i].bound_x + 1;
		touching[1] = element_array[i].y - element_array[i].bound_y;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_y < 0))
		{
			block = MapGetBlockInPixels(touching[0] , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			switch(block->user1)
			{
				case 1:
						element_array[i].y = block_pos[1] + mapblockheight  + element_array[i].bound_y;
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
					break;
				case 4:
					if(touching[1] <=  block_pos[1] + mapblockwidth - (touching[0] - block_pos[0]))
					{
						element_array[i].current_jump_power = 0;
						should_apply_gravity = false;
					}
					break;
				
			}
	
		}


		//======================================================
		// Element going rightward
		//======================================================
	
		touching[0] = element_array[i].x;
		touching[1] = element_array[i].y + element_array[i].bound_y - 1;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x > 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if(block->user1 == 2)
			{
				
				if((element_array[i].vel_y>=0)&&element_array[i].y + element_array[i].bound_y > block_pos[1] +mapblockheight -  (element_array[i].x - block_pos[0]))
				{
					element_array[i].reset_jump();
					should_apply_gravity = false;
					element_array[i].y = block_pos[1] - element_array[i].bound_y + mapblockheight - (element_array[i].x - block_pos[0]);
					
				}	
			}
		}


		touching[0] = element_array[i].x + element_array[i].bound_x+1;
		touching[1] = element_array[i].y + element_array[i].bound_y - 1 ;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x> 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if((block->user1 == 1 || (block->user1 >= 4 && block->user1 <=5)))
			{
				element_array[i].x = block_pos[0]  - element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
		}

		touching[0] = element_array[i].x + element_array[i].bound_x;
		touching[1] = element_array[i].y ;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x> 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if(block->user1 == 1)
			{
				element_array[i].x = block_pos[0]  - element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}

			if(block->user1 == 5 &&
				touching[0] >= block_pos[0] + ((element_array[i].y - element_array[i].bound_y)  - block_pos[1]))
			{
				//element_array[i].x = block_pos[0] + ((element_array[i].y - element_array[i].bound_y) - block_pos[1])  - element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
		}


		touching[0] = element_array[i].x + element_array[i].bound_x;
		touching[1] = element_array[i].y - element_array[i].bound_y + 1 ;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x> 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if(block->user1 == 1)
			{
				element_array[i].x = block_pos[0]  - element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
			if(block->user1 == 5 &&
				touching[0] >= block_pos[0] + (touching[1] - block_pos[1]))
			{
				element_array[i].x = block_pos[0] + (touching[1] - block_pos[1])  - element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
		}
		//======================================================
		// Element going leftward
		//======================================================
	
		touching[0] = element_array[i].x;
		touching[1] = element_array[i].y + element_array[i].bound_y - 1;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x < 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if(block->user1 == 3)
			{
				if((element_array[i].vel_y>=0)&&element_array[i].y + element_array[i].bound_y > block_pos[1] + (element_array[i].x - block_pos[0]))
				{
					element_array[i].reset_jump();
					should_apply_gravity = false;
					element_array[i].y = block_pos[1] - element_array[i].bound_y + (element_array[i].x - block_pos[0]);
					
				}	
			}
		}

		touching[0] = element_array[i].x - element_array[i].bound_x;
		touching[1] = element_array[i].y + element_array[i].bound_y - 1 ;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x< 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if((block->user1 == 1 || (block->user1 >= 4 && block->user1 <=5)))
			{
				element_array[i].x = block_pos[0] + mapblockwidth - 1 +  element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
		}

		touching[0] = element_array[i].x - element_array[i].bound_x;
		touching[1] = element_array[i].y ;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x < 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if(block->user1 == 1)
			{
				element_array[i].x = block_pos[0]  +mapblockwidth - 1 + element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
			if(block->user1 == 4 &&
				touching[0] <= block_pos[0] + mapblockwidth -((element_array[i].y - element_array[i].bound_y)  - block_pos[1]))
			{
				//element_array[i].x = block_pos[0] + ((element_array[i].y - element_array[i].bound_y) - block_pos[1])  - element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
		}


		touching[0] = element_array[i].x - element_array[i].bound_x;
		touching[1] = element_array[i].y - element_array[i].bound_y + 1 ;
		if((touching[0] > 0) && (touching[0]< mapwidth*mapblockwidth) &&											
			(touching[1] > 0) &&  (touching[1] < mapheight*mapblockheight) && 
			(element_array[i].vel_x < 0))
		{
			block = MapGetBlockInPixels(touching[0]  , touching[1]);
			block_pos[1] = MapGetYOffset(touching[0], touching[1])*mapblockheight;
			block_pos[0] = MapGetXOffset(touching[0] , touching[1])*mapblockwidth;
			if(block->user1 == 1)
			{
				element_array[i].x = block_pos[0]  + mapblockwidth - 1 + element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
			if(block->user1 == 4 &&
				touching[0] <= block_pos[0] + mapblockwidth - (touching[1] - block_pos[1]))
			{
				element_array[i].x = block_pos[0] + mapblockwidth - (touching[1] - block_pos[1]) + element_array[i].bound_x;
				element_array[i].vel_x = 0;
			}
		}
		
		if (should_apply_gravity)
			apply_gravity(&element_array[i]);
		else
		{
			if(!in_ladder)
				element_array[i].vel_y = 0;
		}
		
		// Directly related to double jump / jumping after falling from block
		if(element_array[i].vel_y > 0 && !in_ladder)
			element_array[i].current_jump_power = 0;
	
	}
}

void apply_gravity(MY_ELEMENT *element)
{
	float current_vel_y;
	current_vel_y = element->vel_y;
	element->vel_y += GRAVITY;
	if((current_vel_y <= 0) && (element->vel_y > 0))		// there was an inversion in vel_y
		element->last_jump_height = element->y - mapblockheight/2;
}

void change_state(MY_STATE_MACHINE &stm , GAME_STATES next)
{
	//======================================================
	//pre processing change state
	//======================================================
	switch(stm.current_state)
	{
		case MAIN_MENU:
			keys[ESCAPE] = false;
			break;
		case RUN_GAME:
			keys[ESCAPE] = false;
			break;
		case GAME_OVER:
			break;
	}
	stm.current_state = next;
	//======================================================
	//pos processing change state
	//======================================================
	switch(stm.current_state)
	{
		case MAIN_MENU:
			break;
		case RUN_GAME:
			break;
		case GAME_OVER:
			break;
	}
}